package operand

import (
	"fmt"
)

// Int is the wrapped int value.
type Int int64

func (i Int) Int64() int64 {
	return int64(i)
}

func (i Int) Add(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return uint64(i) + o.Uint64(), nil
	case interface{ Int64() int64 }:
		return int64(i) + o.Int64(), nil
	case interface{ Float64() float64 }:
		return float64(i) + o.Float64(), nil
	case fmt.Stringer:
		return fmt.Sprintf("%d%s", i, o), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return i.Add(otherValue)
	}
}

func (i Int) Sub(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return uint64(i) - o.Uint64(), nil
	case interface{ Int64() int64 }:
		return int64(i) - o.Int64(), nil
	case interface{ Float64() float64 }:
		return float64(i) - o.Float64(), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return i.Sub(otherValue)
	}
}

func (i Int) Mul(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return uint64(i) * o.Uint64(), nil
	case interface{ Int64() int64 }:
		return int64(i) * o.Int64(), nil
	case interface{ Float64() float64 }:
		return float64(i) * o.Float64(), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return i.Mul(otherValue)
	}
}

func (i Int) Div(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return uint64(i) / o.Uint64(), nil
	case interface{ Int64() int64 }:
		return int64(i) / o.Int64(), nil
	case interface{ Float64() float64 }:
		return float64(i) / o.Float64(), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return i.Div(otherValue)
	}
}

func (i Int) Mod(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return uint64(i) % o.Uint64(), nil
	case interface{ Int64() int64 }:
		return int64(i) % o.Int64(), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return i.Mod(otherValue)
	}
}

func (i Int) Neg() (any, error) {
	return -int64(i), nil
}

func (i Int) Bool() bool {
	return i != 0
}

func (i Int) Eq(other any) bool {
	return i == other
}

// Uint is the wrapped uint value.
type Uint uint64

func (i Uint) Uint64() uint64 {
	return uint64(i)
}

func (i Uint) Add(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return uint64(i) + o.Uint64(), nil
	case interface{ Int64() int64 }:
		return uint64(i) + uint64(o.Int64()), nil
	case interface{ Float64() float64 }:
		return float64(i) + o.Float64(), nil
	case fmt.Stringer:
		return fmt.Sprintf("%d%s", i, o), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return i.Add(otherValue)
	}
}

func (i Uint) Sub(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return uint64(i) * o.Uint64(), nil
	case interface{ Int64() int64 }:
		return uint64(i) - uint64(o.Int64()), nil
	case interface{ Float64() float64 }:
		return float64(i) - o.Float64(), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return i.Sub(otherValue)
	}
}

func (i Uint) Mul(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return uint64(i) * o.Uint64(), nil
	case interface{ Int64() int64 }:
		return uint64(i) * uint64(o.Int64()), nil
	case interface{ Float64() float64 }:
		return float64(i) * o.Float64(), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return i.Mul(otherValue)
	}
}

func (i Uint) Div(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return uint64(i) / o.Uint64(), nil
	case interface{ Int64() int64 }:
		return uint64(i) / uint64(o.Int64()), nil
	case interface{ Float64() float64 }:
		return float64(i) / o.Float64(), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return i.Div(otherValue)
	}
}

func (i Uint) Mod(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return uint64(i) % o.Uint64(), nil
	case interface{ Int64() int64 }:
		return uint64(i) % uint64(o.Int64()), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return i.Mod(otherValue)
	}
}

func (i Uint) Neg() (any, error) {
	return nil, fmt.Errorf("negative is not supported on unsigned int")
}

func (i Uint) Bool() bool {
	return i != 0
}

func (i Uint) Eq(other any) bool {
	return i == other
}

// Float is the wrapped float value.
type Float float64

func (f Float) Float64() float64 {
	return float64(f)
}

func (f Float) Add(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return float64(f) + float64(o.Uint64()), nil
	case interface{ Int64() int64 }:
		return float64(f) + float64(o.Int64()), nil
	case interface{ Float64() float64 }:
		return float64(f) + o.Float64(), nil
	case fmt.Stringer:
		return fmt.Sprintf("%f%s", f, o), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return f.Add(otherValue)
	}
}

func (f Float) Sub(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return float64(f) - float64(o.Uint64()), nil
	case interface{ Int64() int64 }:
		return float64(f) - float64(o.Int64()), nil
	case interface{ Float64() float64 }:
		return float64(f) - o.Float64(), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return f.Sub(otherValue)
	}
}

func (f Float) Mul(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return float64(f) * float64(o.Uint64()), nil
	case interface{ Int64() int64 }:
		return float64(f) * float64(o.Int64()), nil
	case interface{ Float64() float64 }:
		return float64(f) * o.Float64(), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return f.Mul(otherValue)
	}
}

func (f Float) Div(other any) (any, error) {
	switch o := other.(type) {
	case interface{ Uint64() uint64 }:
		return float64(f) / float64(o.Uint64()), nil
	case interface{ Int64() int64 }:
		return float64(f) / float64(o.Int64()), nil
	case interface{ Float64() float64 }:
		return float64(f) / o.Float64(), nil
	default:
		otherValue, err := New(other)
		if err != nil {
			return nil, err
		}
		return f.Div(otherValue)
	}
}

func (f Float) Mod(other any) (any, error) {
	return nil, fmt.Errorf("remainder is unsupported on float")
}

func (i Float) Neg() (any, error) {
	return -float64(i), nil
}

func (i Float) Bool() bool {
	return i != 0
}

func (i Float) Eq(other any) bool {
	return i == other
}
